/*
 * Copyright (c) 2020, yifengling0. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. No personal names or organizations' names associated with the
 *    Atomthreads project may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include "atom.h"
#include "hw.h"
#include "simSpi.h"
#include "drv_pin.h"
#include "drv_fast_simspi.h"
#include "stm8l15x_gpio.h"

//直接extern，驱动内部函数不直接对外
extern const struct pin_index *get_pin(uint8_t pin);

static int fast_sim_spi_xfer_unit(SimSpi *dev, int val)
{
	int result = 0;
	int i;
    
    FastSimSpi *fast = (FastSimSpi *)dev;
    int bitWidth = dev->Super.Protected.bitWidth;
    int delay = dev->Protected.ctrl->delay;

    CRITICAL_STORE;

    CRITICAL_START ();

	if (dev->Super.Protected.mode & SPI_LSB_FIRST)
	{
		for (i = 0; i < bitWidth; i++)
		{
			GPIO_WriteBit(fast->Protected.scl.gpio, fast->Protected.scl.pin, RESET);
            GPIO_WriteBit(fast->Protected.mosi.gpio, fast->Protected.mosi.pin, ((val & (1 << i)) ? SET : RESET));
            //任何delay都浪费时间这里直接取消，函数调用也会有一部分时间，作为clock的间隔，加快虚拟spi速度
			//HwDelayUs(delay);
            GPIO_WriteBit(fast->Protected.scl.gpio, fast->Protected.scl.pin, SET);
            if(fast->Protected.miso.gpio != NULL){
                result |= ((GPIO_ReadInputData(fast->Protected.miso.gpio) & fast->Protected.scl.pin) ? 1 : 0) << i;
            }
			//HwDelayUs(delay);
		}
	}
	else
	{
		for (i = bitWidth - 1; i >= 0; i--)
		{
			GPIO_WriteBit(fast->Protected.scl.gpio, fast->Protected.scl.pin, RESET);
            GPIO_WriteBit(fast->Protected.mosi.gpio, fast->Protected.mosi.pin, ((val & (1 << i)) ? SET : RESET));
			//HwDelayUs(delay);
            GPIO_WriteBit(fast->Protected.scl.gpio, fast->Protected.scl.pin, SET);
            if( fast->Protected.miso.gpio != NULL){
                result |= ((GPIO_ReadInputData(fast->Protected.miso.gpio) & fast->Protected.scl.pin) ? 1 : 0) << i;
            }
			//HwDelayUs(delay);
		}
	}

	CRITICAL_END();

	return result;
}

static void fill_gpio_config(PinConfig *config, int pin)
{
    if( pin != -1 ){
        config->gpio = get_pin(pin)->gpio;
        config->pin = get_pin(pin)->pin;
    }else{
        config->gpio = NULL;
        config->pin = GPIO_Pin_0;
    }
}

void FastSimSpiCreate(FastSimSpi* dev, const char *name,  int mode, int dataLines, int bitwidth , uint32_t freq, SimSpiDevCtrl* ctrl)
{
    SimSpi *spi = (SimSpi *)dev;

    SimSpiCreate(spi, name, mode, dataLines, bitwidth, freq, ctrl, fast_sim_spi_xfer_unit);

    fill_gpio_config(&dev->Protected.cs, dev->super.Protected.ctrl->cs);
    fill_gpio_config(&dev->Protected.scl, dev->super.Protected.ctrl->scl);
    fill_gpio_config(&dev->Protected.mosi, dev->super.Protected.ctrl->mosi);
    fill_gpio_config(&dev->Protected.miso, dev->super.Protected.ctrl->miso);
}